
#include "nh_spo2.h"
#include "mem_ctrl.h"
#include "assit_func.h"
#include "nh_base_alg.h"
#include "math.h"
#include "kalman_flt.h"

#define DEBUG_INFO       (0x00)    /** 调试信息打印开关 */
#if DEBUG_INFO
#define ALG_PRINTF(fmt, ...)  do{if(DEBUG_INFO) printf(fmt, ##__VA_ARGS__); }while(0) 
#else
#define ALG_PRINTF(fmt, ...)   
#endif  
/**************************/

#define ALG_PPG_SPO2_VERSION_STR   " V_1_0_6"

#define SAMPLE_FREQ_HR   50

/**** 时间  4s 
 *  心率区间   40~200   0.66667Hz ~ 3.3333Hz  [0.6Hz ~ 3.5Hz] -> peak 数量 [ 2~14 ]
 *  心率       100      1.6667Hz              [ 1.7Hz   ]        peak 数量   6~7   
*/
/***************************/

#define MIN_SUPPORT_HR  40
//// 窗口内最大peak数量
#define MAX_SUPPORT_HR  200
#define SAMPLE_WINDOW_SIZE_S   4 ///采样窗口大小 :s
#define RAW_DATA_BUFF_SIZE  (uint16_t)(SAMPLE_WINDOW_SIZE_S*SAMPLE_FREQ_SPO2)
#define MAX_SPO2  100.0f
#define MIN_SPO2  85.0f
#define MAX_R_VAL 0.95f
#define MIN_R_VAL 0.45f


#define R_VAL_CACHE_LEN     (uint16_t)((RAW_DATA_BUFF_SIZE/SAMPLE_FREQ_SPO2)*MAX_SUPPORT_HR/60)  /// 5
#define HR_TICK_CACHE_LEN   R_VAL_CACHE_LEN

#define SPO2_VALID_TIME_S      6   /// SPO2值的有效时长 X秒
#define AC_PI_MIN_RANGE_MULTIPLICATION_1000    1   /// x%

//// 心率计数长度
#define PEAK_VAl_BUFFER_LEN  (uint16_t)(SPO2_VALID_TIME_S*MAX_SUPPORT_HR/60)
//// 血氧保存长度
#define SPO2_SAVE_ITEMS_SIZE  PEAK_VAl_BUFFER_LEN    /// spo2保存的个数最大

typedef struct 
{
    int16_t     peak_diff_time_cache[HR_TICK_CACHE_LEN];  /// 峰间距时间
    int16_t     velly_diff_time_cache[HR_TICK_CACHE_LEN]; /// 谷间距时间
    int16_t     hr_diff_time_cache[HR_TICK_CACHE_LEN*2];  /// 用于计算心率的间距时间
    uint16_t    hr_diff_time_cnt;                         //// 记录的间距时间个数
    uint16_t    velly_val_cnt;                            //// 谷值个数
    uint16_t    peak_val_cnt;                             //// 峰值个数
    uint16_t    cache_size;                               //// 缓存个数
}peak_process_t;
typedef struct 
{
    float       R_val_cache[R_VAL_CACHE_LEN];   //// R值缓存
    uint8_t     R_val_cache_index;              //// 缓存索引
    uint8_t     R_val_cache_size;               //// 缓存个数
}spo2_process_t;


typedef struct {
    int16_t         hr;             //// 心率
    int32_t         *red_raw;       //// 原始数据缓存区起始指针
    int32_t         *ir_raw;
    uint16_t        buff_acount;    //// 缓存个数
    uint16_t        min_distance;
    float           spo2;           /// spo2 val 
    float           R;              ///  R val
    int32_t         red_ac;
    int32_t         ir_ac;
    int32_t         red_dc;
    int32_t         ir_dc;
    uint16_t        sample_freq_Hz;
    spo2_process_t  r_info;
    peak_process_t  hr_info;
} spo2_info_t;




typedef struct {
    float       spo2[SPO2_SAVE_ITEMS_SIZE];     /// spo2 val 
    uint32_t    tick[SPO2_SAVE_ITEMS_SIZE];     /// value  before now ticks
    float       spo2_cache[SPO2_SAVE_ITEMS_SIZE];
    float       spo2_shadow[SPO2_SAVE_ITEMS_SIZE];
    uint16_t    tick_size;                      /// spo2_ size
    uint32_t    oldest_tick_limit;              /// 多少个tick之后，数据无效，自动移除
    uint8_t     tick_buff_size;                 /// tick缓存大小

    uint16_t    hr_timeRes[PEAK_VAl_BUFFER_LEN];
    uint16_t    hr_resShadow[PEAK_VAl_BUFFER_LEN];
    uint16_t    hr_time_limit[PEAK_VAl_BUFFER_LEN];
    uint32_t    hr_tick[PEAK_VAl_BUFFER_LEN];     /// value  before now ticks
    uint16_t    hr_tick_size;  
    uint8_t     hr_tick_buff_size; 
}spo2_time_tick_t;

/**
 * @brief 
 * 
 * @param tick_objs 
 * @param oldest_tick  多少个tick之后，数据无效，自动移除
 */
void time_tick_init(spo2_time_tick_t *tick_objs,uint32_t oldest_tick);
/**
 * @brief 添加tick spo2
 * @param tick_objs 
 */
void time_tick_save(spo2_time_tick_t *tick_objs,float spo2_curt);

/**
 * @brief 
 * 
 * @param tick_objs 
 * @param hr_curt 新增的HR 
 */
void time_tick_save_hr(spo2_time_tick_t *tick_objs,uint16_t hr_curt);


/**
 * @brief 主函数更新tick
 * 
 * @param tick_objs 
 */
void time_ticks_update_tick(spo2_time_tick_t *tick_objs);





/**
 * @brief tick info 
 */
static spo2_time_tick_t tick_handle;



float diff_coffient_gen(float spo2);




char *get_spo2_algorithm_version(void)
{
    return ALG_PPG_SPO2_VERSION_STR;
}

/**
 * @brief 检测波峰和波谷,数据缓存
 */

#define COFFIENT_BUFF_SIZE 3
static float spo2_coffient_buffer[COFFIENT_BUFF_SIZE];

/**
 * @brief 
 *  find max & min in {a,b,c,d}
 * @param a 
 * @param b 
 * @param c 
 * @param d 
 * @param max result
 * @param min 
 */
void find_extremes(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t *max, uint8_t *min);

/**
 * @brief output result
 * 
 * @param r_val 
 * @param coff_val 
 * @return float 
 */


// h0011
#define Cir -85.22349706132235f-5.8f
#define Cr -237.44956560165494f
#define Cv 12.672732219894312f-3.0f



// const float coff[5]={-514.54224633,  1413.08483836, -1452.04010674,628.22274523,3.69977718};


/// @brief y = 352.46x4 - 878.51x3 + 758.31x2 - 289.48x + 142.17

// const float coff[5]={352.46f,     -878.51f,  758.31f,      -289.48f,   142.17f };
// const float coff[5]={-356.924f,   602.722f,  -251.810f,    -53.619f,   136.956f };  //  -356.924   602.722  -251.810   -53.619   136.956  ///2024年5月16日 10:48:05
// const float coff[5]={ -602.966f,  1320.631f, -1027.966f,   314.622f,   72.309f };  //  -602.966   1320.631  -1027.966    314.622     72.309 /// 2024年5月16日 11:04:34
// const float coff[5]={ -707.838f,   1625.299f,  -1353.006f,    464.540f,   47.369f}; // -707.838f,   1625.299f,  -1353.006f,    464.540f,     47.369f /// 2024年5月16日 11:11:38

//  -707.838   1625.299  -1353.006    464.540     46.969
const float coff[5]={ -707.838f,   1625.299f,  -1353.006f,    464.540f,   46.969f};

static float spo2_calc(float r_val,float *coff_val)
{
/*** 二次拟合方式 ***/
    float r_val_coff = 0.0;
    // r_val_coff = (r_val*r_val*coff_val[0]+coff_val[1]*r_val+coff_val[2]);
    // if(r_val_coff>100.0f)
    // {
    //     r_val_coff = 100.0f;
    // }
    // if(r_val_coff<85.0f)
    // {
    //     r_val_coff = 85.0f;
    // }

    r_val_coff = powf(r_val,4)*coff[0]+powf(r_val,3)*coff[1]+powf(r_val,2)*coff[2]+r_val*coff[3]+coff[4];
    if(r_val_coff>100.0f)
    {
        r_val_coff = 100.0f;
    }
    if(r_val_coff<85.0f)
    {
        r_val_coff = 85.0f;
    }
    return r_val_coff;
/*** 曲线公式计算 ***/

    // float spo2 = ((10.0f*Cir+2)*r_val-10.0f*Cr+10)/(2.0f*r_val+19.0f) + r_val * r_val * Cv;
    // if (spo2 > 100.0f) 
    // {
    //     spo2 = 100.0;
    // } 
    // else if ( (spo2 < 85.0f) || ( (int)(r_val*100.0f) < 10) ) 
    // {
    //     spo2 = 85.0f;
    // }
    // return (float)spo2;
}

/// @brief 开辟空间,原始数据
int32_t red_raw_data[RAW_DATA_BUFF_SIZE];
int32_t ir_raw_data [RAW_DATA_BUFF_SIZE];

spo2_info_t spo2_info;

/// @brief 初始化卡尔曼滤波器 
float init_x_spo2[2] = {85, 0};
float init_p_spo2[2][2] = {{10e-3 , 0}, {0 , 10e-3}};
static kalman2_state spo2_state;

/// @brief 初始化卡尔曼滤波器 心率计算
float init_x_hr[2] = {75, 0};
float init_p_hr[2][2] = {{10e-3 , 0}, {0 , 10e-3}};
static kalman2_state hr_state;


void spo2_init(void)
{   
    spo2_info.hr=0;
    spo2_info.red_raw = red_raw_data;
    spo2_info.ir_raw = ir_raw_data;
    spo2_info.buff_acount = RAW_DATA_BUFF_SIZE;
    spo2_info.min_distance = SAMPLE_FREQ_SPO2*60/MAX_SUPPORT_HR-1;
    spo2_info.spo2 = 99;
    spo2_info.R = 0.3;
    spo2_info.red_ac = 0;
    spo2_info.ir_ac = 0;
    spo2_info.red_dc = 0;
    spo2_info.ir_dc = 0;
    spo2_info.sample_freq_Hz = SAMPLE_FREQ_SPO2;
    spo2_info.hr_info.peak_val_cnt=0;
    spo2_info.hr_info.cache_size=HR_TICK_CACHE_LEN;

    spo2_info.r_info.R_val_cache_size = R_VAL_CACHE_LEN;
    spo2_info.r_info.R_val_cache_index = 0;

    spo2_coffient_buffer[0] = -56.184;
    spo2_coffient_buffer[1] = 43.589 ;
    spo2_coffient_buffer[2] = 91.457 ;

/** show all info **/
    ALG_PRINTF("spo2_info.hr %d\n",spo2_info.hr);
    ALG_PRINTF("spo2_info.buff_acount %d\n",spo2_info.buff_acount);
    ALG_PRINTF("spo2_info.min_distance %d\n",spo2_info.min_distance);
    ALG_PRINTF("spo2_info.spo2 %f\n",spo2_info.spo2);
    ALG_PRINTF("spo2_info.R %f\n",spo2_info.R);
    ALG_PRINTF("spo2_info.red_ac %d\n",spo2_info.red_ac);
    ALG_PRINTF("spo2_info.ir_ac %d\n",spo2_info.ir_ac);
    ALG_PRINTF("spo2_info.red_dc %d\n",spo2_info.red_dc);
    ALG_PRINTF("spo2_info.ir_dc %d\n",spo2_info.ir_dc);
    ALG_PRINTF("spo2_info.sample_freq_Hz %d\n",spo2_info.sample_freq_Hz);
    ALG_PRINTF("spo2_info.r_info.R_val_cache_size %d\n",spo2_info.r_info.R_val_cache_size);
    ALG_PRINTF("spo2_info.r_info.R_val_cache_index %d\n",spo2_info.r_info.R_val_cache_index);

    ALG_PRINTF("spo2_coffient_buffer[0] %f\n",spo2_coffient_buffer[0]);
    ALG_PRINTF("spo2_coffient_buffer[1] %f\n",spo2_coffient_buffer[1]);
    ALG_PRINTF("spo2_coffient_buffer[2] %f\n",spo2_coffient_buffer[2]);

    time_tick_init(&tick_handle,(uint32_t)(spo2_info.sample_freq_Hz*SPO2_VALID_TIME_S));

    kalman2_init(&spo2_state,init_x_spo2,init_p_spo2);
    kalman2_init(&hr_state,init_x_hr,init_p_hr);

}

static float temp_red_pi = 0.0;
static float temp_ir_pi = 0.0;
// static float temp_r_value = 0.0;

static float r_val_shadow_list[R_VAL_CACHE_LEN];


static spo2_result_t result_info;

spo2_result_t output_spo2_result(void)
{
    result_info.hr = spo2_info.hr;
    result_info.spo2 = spo2_info.spo2;
    return result_info;
}

float get_spo2_r_val(void)
{
    return spo2_info.R;
}


static uint16_t raw_cnt = 0;

static int32_t  _loc_peak_left  [RAW_DATA_BUFF_SIZE];
static int32_t  _loc_peak_right [RAW_DATA_BUFF_SIZE];
static uint16_t _loc_peak_mid   [RAW_DATA_BUFF_SIZE];

static int32_t  _loc_peak_shadow[RAW_DATA_BUFF_SIZE];
static uint8_t  _loc_res_keep   [RAW_DATA_BUFF_SIZE];


/// @brief  保存peak位置 index , 4对变量
int32_t red_peak_val_buffer[PEAK_VAl_BUFFER_LEN];
uint8_t red_peak_val_len;
int32_t red_velley_val_buffer[PEAK_VAl_BUFFER_LEN];
uint8_t red_velley_val_len;
int32_t ir_peak_val_buffer[PEAK_VAl_BUFFER_LEN];
uint8_t ir_peak_val_len;
int32_t ir_velley_val_buffer[PEAK_VAl_BUFFER_LEN];
uint8_t ir_velley_val_len;

void spo2_input(int32_t red,int32_t ir)
{
    move_int32(spo2_info.red_raw,1,spo2_info.buff_acount);
    spo2_info.red_raw[spo2_info.buff_acount-1] = red;
    move_int32(spo2_info.ir_raw,1,spo2_info.buff_acount);
    spo2_info.ir_raw[spo2_info.buff_acount-1] = ir;
    raw_cnt++;
    // ALG_PRINTF("spo2_input red %d ir %d\n",red,ir);
    uint32_t peak_cnt_red = 0, peak_cnt_ir = 0;
    if(raw_cnt >= spo2_info.buff_acount)
    {
        // red_mean = mean_fun(spo2_info.red_raw,spo2_info.buff_acount);
        // red_range = (int32_t)(0.25f* (float)(listmax(spo2_info.red_raw,spo2_info.buff_acount)-listmin(spo2_info.red_raw,spo2_info.buff_acount)));
        // ir_mean = mean_fun(spo2_info.ir_raw,spo2_info.buff_acount);
        // ir_range = (int32_t)(0.25f*(float)(listmax(spo2_info.ir_raw,spo2_info.buff_acount)-listmin(spo2_info.ir_raw,spo2_info.buff_acount)));

        raw_cnt = spo2_info.buff_acount-1 ;
        uint8_t half_range = (spo2_info.min_distance+2)>>1;
        for(uint16_t i=0;i<spo2_info.buff_acount;i++) ///// 信号反转，寻找谷值
        {
            spo2_info.red_raw[i] =spo2_info.red_raw[i]*-1;
        }
        
        //// peak 个数    //// RED  VELLY
        peak_cnt_red = findpeaks(spo2_info.red_raw,
                                 spo2_info.buff_acount,
                                 _loc_peak_mid,
                                 _loc_peak_left,
                                 _loc_peak_shadow,
                                 _loc_peak_right,  ///// peak 位置结果
                                 _loc_res_keep,
                                 spo2_info.min_distance);
        red_velley_val_len = 0;
        //// 保存 peak loc
        for(uint16_t i=0;i<PEAK_VAl_BUFFER_LEN;i++){red_velley_val_buffer[i]=0;}
        for(uint16_t i=0;i<peak_cnt_red && i<PEAK_VAl_BUFFER_LEN;i++)
        {
          ///  ALG_PRINTF("RED Velly:[%2d]->[%9d] limit:[%9d]\r\n",_loc_peak_right[i],-red_raw_data[_loc_peak_right[i]],(red_mean-red_range));
            // if(red_raw_data[_loc_peak_right[i]] < -1*(red_mean-red_range))
            if( ((_loc_peak_right[i]-half_range) >0 ) && (_loc_peak_right[i]+ half_range) ) /// 检查访问越界
            {   //// ensure peak
                if( (spo2_info.red_raw[_loc_peak_right[i] + half_range] < spo2_info.red_raw[_loc_peak_right[i]]) && (spo2_info.red_raw[_loc_peak_right[i] - half_range] < spo2_info.red_raw[_loc_peak_right[i]]) )
                {
                    red_velley_val_buffer[red_velley_val_len] = _loc_peak_right[i];
                    red_velley_val_len++;
                }
            }
        }

        for(uint16_t i=0;i<spo2_info.buff_acount;i++) /// 反转回去，找峰值
        {
            spo2_info.red_raw[i] =spo2_info.red_raw[i]*-1;
        }
        /// RED PEAK
        peak_cnt_red = findpeaks(spo2_info.red_raw,
                                 spo2_info.buff_acount,
                                 _loc_peak_mid,
                                 _loc_peak_left,
                                 _loc_peak_shadow,
                                 _loc_peak_right, 
                                 _loc_res_keep,
                                 spo2_info.min_distance);
        red_peak_val_len = 0;
        for(uint16_t i=0;i<PEAK_VAl_BUFFER_LEN;i++){red_peak_val_buffer[i]=0;}
        for(uint16_t i=0;i<peak_cnt_red && i<PEAK_VAl_BUFFER_LEN;i++)
        {
            //// if(red_raw_data[_loc_peak_right[i]]>(red_mean+red_range))
            if( ((_loc_peak_right[i]-half_range) >0 ) && (_loc_peak_right[i]+ half_range) ) /// 检查访问越界
            {   //// ensure peak
                if( (spo2_info.red_raw[_loc_peak_right[i] + half_range] < spo2_info.red_raw[_loc_peak_right[i]]) && (spo2_info.red_raw[_loc_peak_right[i] - half_range] < spo2_info.red_raw[_loc_peak_right[i]]) )
                {
            // {
                    red_peak_val_buffer[red_peak_val_len] = _loc_peak_right[i];
                    red_peak_val_len++;
                }
            }
        }
/**********for ir *****************/
        for(uint16_t i=0;i<spo2_info.buff_acount;i++) //// 找谷值
        {
            spo2_info.ir_raw[i] =spo2_info.ir_raw[i]*-1;
        }
        //// IR VELLY
        peak_cnt_ir = findpeaks(spo2_info.ir_raw,
                                 spo2_info.buff_acount,
                                 _loc_peak_mid,
                                 _loc_peak_left,
                                 _loc_peak_shadow,
                                 _loc_peak_right,
                                 _loc_res_keep,
                                 spo2_info.min_distance);
        ir_velley_val_len = 0;
        spo2_info.hr_info.velly_val_cnt=0;
        spo2_info.hr_info.hr_diff_time_cnt=0;
        for(uint16_t i=0;i<PEAK_VAl_BUFFER_LEN;i++){ir_velley_val_buffer[i]=0;}
        for(uint16_t i=0;i< peak_cnt_ir && i<PEAK_VAl_BUFFER_LEN;i++)
        {
            /// if(ir_raw_data[_loc_peak_right[i]] < -1*(ir_mean-ir_range))
            if( ((_loc_peak_right[i]-half_range) >0 ) && (_loc_peak_right[i]+ half_range) ) /// 检查访问越界
            {   //// ensure peak
                if( (ir_raw_data[_loc_peak_right[i] + half_range] < ir_raw_data[_loc_peak_right[i]]) && (ir_raw_data[_loc_peak_right[i] - half_range] < ir_raw_data[_loc_peak_right[i]]) )
                {
                    ir_velley_val_buffer[ir_velley_val_len] = _loc_peak_right[i];
                    
                    if( (ir_velley_val_len>0)&&( spo2_info.hr_info.velly_val_cnt<spo2_info.hr_info.cache_size) )
                    {
                        spo2_info.hr_info.velly_diff_time_cache[spo2_info.hr_info.velly_val_cnt] = ir_velley_val_buffer[ir_velley_val_len]-ir_velley_val_buffer[ir_velley_val_len-1];
                        ALG_PRINTF("Velly diff time [%2d] -> [%2d]\n",spo2_info.hr_info.velly_val_cnt,spo2_info.hr_info.velly_diff_time_cache[spo2_info.hr_info.velly_val_cnt] );
                        spo2_info.hr_info.hr_diff_time_cache[spo2_info.hr_info.hr_diff_time_cnt] = spo2_info.hr_info.velly_diff_time_cache[spo2_info.hr_info.velly_val_cnt];
                        spo2_info.hr_info.hr_diff_time_cnt++;
                        spo2_info.hr_info.velly_val_cnt++;
                    }
                    ir_velley_val_len++;
                    // velly_diff_time_cache
                }
            }
            
        }

        for(uint16_t i=0;i<spo2_info.buff_acount;i++)
        {
            spo2_info.ir_raw[i] =spo2_info.ir_raw[i]*-1;
        }
        /// IR PEAK
        peak_cnt_ir = findpeaks( spo2_info.ir_raw,
                                 spo2_info.buff_acount,
                                 _loc_peak_mid,
                                 _loc_peak_left,
                                 _loc_peak_shadow,
                                 _loc_peak_right,
                                 _loc_res_keep,
                                 spo2_info.min_distance);
        //// 筛选IR Peak 计算峰值
        ir_peak_val_len = 0;
        spo2_info.hr_info.peak_val_cnt=0;
        for(uint16_t i=0;i<PEAK_VAl_BUFFER_LEN;i++){ir_peak_val_buffer[i]=0;}  //// copy peak result to buffer
        for(int16_t i=0; i<(uint16_t)peak_cnt_ir && (i<PEAK_VAl_BUFFER_LEN) ;i++)
        { 
           /// ALG_PRINTF("HR IR_Peak [%9d] Loc:[%2d]\tupper limit:[%9d]\n",ir_raw_data[_loc_peak_right[i]],_loc_peak_right[i], (ir_mean+ir_range) );   
            if( ((_loc_peak_right[i]-half_range) >0 ) && ((_loc_peak_right[i]+ half_range)<spo2_info.buff_acount) ) /// 检查访问越界
            {   //// ensure peak
                if( (ir_raw_data[_loc_peak_right[i] + half_range] < ir_raw_data[_loc_peak_right[i]]) && (ir_raw_data[_loc_peak_right[i] - half_range] < ir_raw_data[_loc_peak_right[i]]) )
                {
                    ir_peak_val_buffer[ir_peak_val_len] = _loc_peak_right[i] ;  //// 拷贝峰值点位置信息   
                    ALG_PRINTF("IR Peak Loc:[%2d]\n",_loc_peak_right[i]);
                    
                    if( (i>0) && (peak_cnt_ir> 1) && (spo2_info.hr_info.peak_val_cnt < spo2_info.hr_info.cache_size) && (ir_peak_val_len>0)) /// 计算peak 间距时间
                    {   
                        spo2_info.hr_info.peak_diff_time_cache[spo2_info.hr_info.peak_val_cnt] =  ir_peak_val_buffer[ir_peak_val_len]-ir_peak_val_buffer[ir_peak_val_len-1];
                        ALG_PRINTF("calc diff time [%2d] -> [%2d]\n",spo2_info.hr_info.peak_val_cnt,spo2_info.hr_info.peak_diff_time_cache[spo2_info.hr_info.peak_val_cnt]);
                        spo2_info.hr_info.hr_diff_time_cache[spo2_info.hr_info.hr_diff_time_cnt] = spo2_info.hr_info.peak_diff_time_cache[spo2_info.hr_info.peak_val_cnt];
                        spo2_info.hr_info.hr_diff_time_cnt++;
                        spo2_info.hr_info.peak_val_cnt++;
                    }
                    ir_peak_val_len++;
                }
            }
        }
        float hr_float;
        ////// 有效值检查
        ALG_PRINTF("峰谷间距有效性检查前->\n");
        for(uint16_t ix=0;ix<spo2_info.hr_info.hr_diff_time_cnt;ix++)
        {
            ALG_PRINTF("-> HR_Info diff time [%2d] -> [%2d]\n",ix,spo2_info.hr_info.hr_diff_time_cache[ix]);
        }
        time_valid_check((uint16_t*)spo2_info.hr_info.hr_diff_time_cache,spo2_info.hr_info.hr_diff_time_cnt);
        ALG_PRINTF("峰谷间距有效性检查后->\n");
        for(uint16_t ix=0;ix<spo2_info.hr_info.hr_diff_time_cnt;ix++)
        {
            ALG_PRINTF("-> Check HR_Info diff time [%2d] -> [%2d]\n",ix,spo2_info.hr_info.hr_diff_time_cache[ix]);
        }
        if(spo2_info.hr_info.peak_val_cnt && spo2_info.hr_info.velly_val_cnt)
        {
            ALG_PRINTF("------ spo2_info.hr_info.peak_val_cnt -- : %d\n",spo2_info.hr_info.peak_val_cnt);
            spo2_info.hr=0;
            peak_cnt_ir = 0; //// 变量引用, 用于间隔时间计数
            for(uint16_t i=0;i<spo2_info.hr_info.hr_diff_time_cnt;i++)  ////  peak diff time add
            {
                if(spo2_info.hr_info.hr_diff_time_cache[i])
                {
                spo2_info.hr += spo2_info.hr_info.hr_diff_time_cache[i];  
                ALG_PRINTF("use diff time [%2d]\n",spo2_info.hr_info.hr_diff_time_cache[i]);
                peak_cnt_ir++;
                }
            }
            if( peak_cnt_ir < 2 ) ///// 补回 检查后的数据
            {
                peak_cnt_ir=0;
                spo2_info.hr=0;
                for(uint16_t i=0;i<spo2_info.hr_info.peak_val_cnt;i++)
                {
                    spo2_info.hr += spo2_info.hr_info.peak_diff_time_cache[i];   
                }
                for( uint16_t i=0;i<spo2_info.hr_info.velly_val_cnt;i++ )
                {
                    spo2_info.hr += spo2_info.hr_info.velly_diff_time_cache[i];
                }
                peak_cnt_ir = spo2_info.hr_info.peak_val_cnt + spo2_info.hr_info.velly_val_cnt ;
                ALG_PRINTF("diff_time failed: -> sum_time:[%3d] cnt[%2d]\n",spo2_info.hr,peak_cnt_ir);
            }
            ALG_PRINTF("------ Updata HR --------\n");
            hr_float = ((float)(spo2_info.sample_freq_Hz*60)/(float)((float)spo2_info.hr/(float)peak_cnt_ir));
            spo2_info.hr = (int16_t)((float)(spo2_info.sample_freq_Hz*60)/(float)((float)spo2_info.hr/(float)peak_cnt_ir));
            // spo2_info.hr = filter_hr_spo2((float)spo2_info.hr,hr_x_buff,hr_y_buff);

            spo2_info.hr = (int16_t) kalman2_filter(&hr_state,(float)spo2_info.hr);
            ALG_PRINTF("Dep diff_time HR: %2d\n",spo2_info.hr);
            time_tick_save_hr(&tick_handle,(uint16_t)spo2_info.hr);
            if(tick_handle.hr_tick_size>3)
            {   
                for(uint16_t i=0;i<tick_handle.hr_tick_size;i++)
                {
                    tick_handle.hr_resShadow[i] = tick_handle.hr_timeRes[i];
                }
                // InsertionSort_uint16(tick_handle.hr_resShadow,tick_handle.hr_tick_size);
                for(uint16_t i=0;i<tick_handle.hr_tick_size;i++)
                {
                    ALG_PRINTF("hr_resShadow[%2d] = [%3d]\n",i,tick_handle.hr_resShadow[i]);
                }
                spo2_info.hr = mean_value_uint16_Weighted( tick_handle.hr_resShadow,tick_handle.hr_tick_size);
                tick_handle.hr_resShadow[tick_handle.hr_tick_size-1] = spo2_info.hr; //// 最新的值更新到 缓存区

                peak_cnt_red = listmax_uint16(tick_handle.hr_resShadow,tick_handle.hr_tick_size) - listmin_uint16(tick_handle.hr_resShadow,tick_handle.hr_tick_size) ;
                
                if(peak_cnt_red>5) //// 记录的数据，差异较大启用 diff_lim
                {
                    spo2_info.hr = diff_lim(tick_handle.hr_resShadow,tick_handle.hr_tick,tick_handle.hr_tick_size,diff_lim_ratio_dynamic(spo2_info.hr),50.0f);
                }
                if(spo2_info.hr<MIN_SUPPORT_HR) { spo2_info.hr=MIN_SUPPORT_HR;}
                if(spo2_info.hr>MAX_SUPPORT_HR) { spo2_info.hr=MAX_SUPPORT_HR;}
                ALG_PRINTF("HR: %2d  %3.3f \n",spo2_info.hr, hr_float);       
            }else
            {
                spo2_info.hr=0;
            }
            
            raw_cnt -= (uint32_t)(1.8f*(_loc_peak_right[1]-_loc_peak_right[0]))>>1;
            ALG_PRINTF("Cut off [%3d]\n",(_loc_peak_right[1]-_loc_peak_right[0])>>1);
            if(spo2_info.hr>100 && spo2_info.hr<180)
            {
                spo2_info.min_distance = (uint16_t)(0.85f*(float)(60*spo2_info.sample_freq_Hz)/(float)spo2_info.hr);
                ALG_PRINTF("---- HR Update distance:[%3d]----",spo2_info.min_distance);
            }
            if(spo2_info.hr<100)
            {
                spo2_info.min_distance = 20;
                ALG_PRINTF("----No Lim Update distance:[%3d]----",spo2_info.min_distance);
            }
            ALG_PRINTF("\n");
        }
        /*********  update HR  ************/
        uint8_t max_len=0,min_len=0,diff_range=0;
        find_extremes(red_peak_val_len,red_velley_val_len,ir_peak_val_len,ir_velley_val_len,&max_len,&min_len);

#if DEBUG_INFO
        ALG_PRINTF("------------------------------- u p d a t e --------------------------------------\n");
        ALG_PRINTF("red_p_cnt:[%2d]\tred_v_cnt:[%d]\tIR_p_cnt:[%2d]\tIR_v_cnt:[%d]\n",red_peak_val_len,red_velley_val_len,ir_peak_val_len,ir_velley_val_len);
        ALG_PRINTF("------------------------------------------------------------------------\n");
        for(uint16_t i=0;i<max_len;i++)
        {
            ALG_PRINTF("->[%d] red_peak:[%3d]\tred_velly:[%3d]\tIR_peak:[%3d]\tIR_velly:[%3d]\n",i+1,red_peak_val_buffer[i],red_velley_val_buffer[i],ir_peak_val_buffer[i],ir_velley_val_buffer[i]);
        }
#endif

        
        ALG_PRINTF("max %2d\tmin %2d\n",max_len,min_len);

        // min_len = ir _peak_val_len < ir_velley_val_len ? ir_peak_val_len : ir_velley_val_len;

        if( min_len > 2 && (spo2_info.hr!=0) )  //// 1 = (floor)(40/60*3[时长3秒])
        {
            max_len = (spo2_info.sample_freq_Hz*60)/( (uint16_t)spo2_info.hr ); /// 计算 diff
            diff_range = (uint8_t)(max_len*0.4) ; //*0.7f); /// 预估的 diff
            ALG_PRINTF("max_len %2d\t diff_range %2d\n" , max_len,diff_range );
            ALG_PRINTF("red_peak_val_len %2d\t ir_peak_val_len %2d\t"    ,red_peak_val_len,ir_peak_val_len);
            ALG_PRINTF("red_velley_val_len %2d\t ir_velley_val_len %2d\n",red_velley_val_len,ir_velley_val_len);
            
            //    if((red_peak_val_len==ir_peak_val_len)&&(red_velley_val_len==ir_velley_val_len)) /// peak velly couple pair
            //    {  
                spo2_info.ir_ac=0;
                spo2_info.red_ac=0;
                _loc_peak_left[1] = 0 ;   /// ir AC
                _loc_peak_left[2] = 0 ;  /// IR DC
                _loc_peak_left[3] = 0 ;  /// RED AC
                _loc_peak_left[4] = 0 ;  /// RED DC

                for(uint8_t i=0;i<min_len;i++)  /// 检查峰峰相位差 
                {
                    
                    ALG_PRINTF("red_peak_loc[%2d] :[%5d]\tir_peak_loc[%2d] :[%5d]\n",i,red_peak_val_buffer[i],i,ir_peak_val_buffer[i]);
                    if( abs(red_peak_val_buffer[i]-ir_peak_val_buffer[i]) < 13 ) /// RED_Peak index IR_Peak index
                    {
                        ALG_PRINTF("    ir_velley_val_buffer[%2d] :[%5d]\tir_peak_val_buffer[%2d] :[%5d]\n",i,ir_velley_val_buffer[i],i,ir_peak_val_buffer[i]);
                        if(  ir_velley_val_buffer[i] < (ir_peak_val_buffer[i]+diff_range) )
                        {
    spo2_info.ir_ac += (ir_raw_data[ir_peak_val_buffer[i]]-ir_raw_data[ir_velley_val_buffer[i]])*2;
    spo2_info.ir_ac += ir_raw_data[ir_peak_val_buffer[i]+1]-ir_raw_data[ir_velley_val_buffer[i]+1];
    spo2_info.ir_ac += ir_raw_data[ir_peak_val_buffer[i]-1]-ir_raw_data[ir_velley_val_buffer[i]-1];
    spo2_info.ir_ac = spo2_info.ir_ac>>2;
    spo2_info.ir_dc = (ir_raw_data[ir_peak_val_buffer[i]]+2*ir_raw_data[(ir_peak_val_buffer[i]+ir_velley_val_buffer[i])>>1]+ir_raw_data[ir_velley_val_buffer[i]])>>2;
                            ALG_PRINTF("        IR_Peak val:[%5d]\tIR_Velly:[%5d]\n",ir_raw_data[ir_peak_val_buffer[i]],ir_raw_data[ir_velley_val_buffer[i]]);
                            ALG_PRINTF("        ir_ac: [%5d] ir_dc:[%5d]\n",spo2_info.ir_ac,spo2_info.ir_dc);
                        _loc_peak_left[1]+=spo2_info.ir_ac;
                        _loc_peak_left[2]+=spo2_info.ir_dc;

                        }
                        if(red_velley_val_buffer[i] < (red_peak_val_buffer[i]+diff_range) )
                        {
    spo2_info.red_ac += (red_raw_data[red_peak_val_buffer[i]]-red_raw_data[red_velley_val_buffer[i]])*2;
    spo2_info.red_ac += red_raw_data[red_peak_val_buffer[i]+1]-red_raw_data[red_velley_val_buffer[i]+1];
    spo2_info.red_ac += red_raw_data[red_peak_val_buffer[i]-1]-red_raw_data[red_velley_val_buffer[i]-1];
    spo2_info.red_ac = spo2_info.red_ac>>2;
    spo2_info.red_dc = (red_raw_data[red_peak_val_buffer[i]]+2*red_raw_data[(red_peak_val_buffer[i]+red_velley_val_buffer[i])>>1]+red_raw_data[red_velley_val_buffer[i]])>>2;
                            ALG_PRINTF("        RED_Peak val:[%5d]\tRED_Velly:[%5d]\n",red_raw_data[red_peak_val_buffer[i]],red_raw_data[red_velley_val_buffer[i]]);
                            ALG_PRINTF("        red_ac: [%5d] red_dc:[%5d]\n",spo2_info.red_ac,spo2_info.red_dc);
                        _loc_peak_left[3]+=spo2_info.red_ac;
                        _loc_peak_left[4]+=spo2_info.red_dc;
                        }
                    }
                }
                spo2_info.ir_ac = _loc_peak_left[1];
                spo2_info.ir_dc =_loc_peak_left[2];
                spo2_info.red_ac =_loc_peak_left[3];
                spo2_info.red_dc = _loc_peak_left[4];  

                ALG_PRINTF("--> AC  OK  RED_AC:[%d]\tIR_AC:[%d]\n",spo2_info.red_ac,spo2_info.ir_ac);
                if(spo2_info.ir_ac && spo2_info.red_ac)
                {
                    temp_red_pi = (float)spo2_info.red_ac/(float)spo2_info.red_dc;
                    temp_ir_pi = (float)spo2_info.ir_ac/(float)spo2_info.ir_dc;
                    spo2_info.R = temp_red_pi/temp_ir_pi;
                    if(((uint32_t)(spo2_info.R*1000.0f))>((uint32_t)(MAX_R_VAL*1000.0f)))  //// 限制R输出区间
                    {
                        spo2_info.R = MAX_R_VAL;
                    }else if((uint32_t)(spo2_info.R*1000.0f)<((uint32_t)(MIN_R_VAL*1000.0f)))
                    {
                        spo2_info.R = MIN_R_VAL;
                    }
                    // spo2_info.r_info.R_val_cache[spo2_info.r_info.R_val_cache_index] = spo2_info.R;
                    ALG_PRINTF("--- R update :[%.4f] red_pi:[%.4f]/ir_pi:[%.4f] \n", spo2_info.R,temp_red_pi,temp_ir_pi);

                    if((uint16_t)(temp_red_pi*1000.0f)>= AC_PI_MIN_RANGE_MULTIPLICATION_1000  && (uint16_t)(temp_ir_pi*1000.0f)>= AC_PI_MIN_RANGE_MULTIPLICATION_1000)
                    {
                        
                        spo2_info.r_info.R_val_cache[spo2_info.r_info.R_val_cache_index] = spo2_info.R;
                        spo2_info.R=0;
                       
                        spo2_info.r_info.R_val_cache_index++;
                        if(spo2_info.r_info.R_val_cache_index>=spo2_info.r_info.R_val_cache_size)
                        {
                            for(max_len=0;max_len<(spo2_info.r_info.R_val_cache_size-1);max_len++)
                            {
                                spo2_info.r_info.R_val_cache[max_len] = spo2_info.r_info.R_val_cache[max_len+1];
                                // spo2_info.R += spo2_info.r_info.R_val_cache[max_len];
                            }
                            spo2_info.r_info.R_val_cache_index= spo2_info.r_info.R_val_cache_size-1 ;
                        }
                        
                        if(spo2_info.r_info.R_val_cache_index<=(spo2_info.r_info.R_val_cache_size))
                        {
                            for(uint8_t ix=0;ix<spo2_info.r_info.R_val_cache_size;ix++) /// copy raw to shadow
                            {
                                r_val_shadow_list[ix] = spo2_info.r_info.R_val_cache[ix];
                                ALG_PRINTF("R Shadow:[%d]->[%3.3f]\n",ix,r_val_shadow_list[ix]);
                            }
                            // InsertionSort_float(r_val_shadow_list,spo2_info.r_info.R_val_cache_index);
                            spo2_info.R =0;
                            for(uint8_t ix=0;ix<spo2_info.r_info.R_val_cache_index;ix++)
                            {
                                spo2_info.R += r_val_shadow_list[ix];
                            }
                            ALG_PRINTF(">>>[spo2_info.r_info.R_val_cache_index ]:[%d]\n",spo2_info.r_info.R_val_cache_index);
                            // spo2_info.r_info.R_val_cache_index = spo2_info.r_info.R_val_cache_size-1;
                            spo2_info.R = spo2_info.R/(float)(spo2_info.r_info.R_val_cache_index);
                            ALG_PRINTF("----------E n d __ R v a l--------------\n");
                            spo2_info.spo2 = spo2_calc(spo2_info.R,spo2_coffient_buffer);
                            
                            spo2_info.spo2 = kalman2_filter(&spo2_state,spo2_info.spo2);

                            time_tick_save(&tick_handle,spo2_info.spo2);
                            if( tick_handle.tick_size>3)  ///// 2 (3) + 3( 2 )
                            {
                                for (uint8_t ix=0;ix<tick_handle.tick_size;ix++)
                                {
                                   tick_handle.spo2_shadow[ix] = tick_handle.spo2[ix+1];
                                }
                                InsertionSort_float( tick_handle.spo2_shadow , tick_handle.tick_size);
                                spo2_info.spo2 = mean_value_float( &tick_handle.spo2_shadow[2] , tick_handle.tick_size-2 );
                                if(spo2_info.spo2>100.0f)
                                {   
                                    spo2_info.spo2 = 100.0f;
                                }    
                                if(spo2_info.spo2<85.0f)
                                {   
                                    spo2_info.spo2 = 85.0f;
                                }   
                            }else
                            {
                                spo2_info.spo2 = 0.0f;
                            }
                            ALG_PRINTF("--->>> calc_spo2: [%2.3f]\n",spo2_info.spo2);
                                      
                            ALG_PRINTF( "[R]:[%2.3f]\t SpO2:[%3.3f]\n",spo2_info.R,spo2_info.spo2);
                        }
                    }
                }
            // }
        }
    }
    time_ticks_update_tick(&tick_handle);
}



void find_extremes(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t *max, uint8_t *min) 
{
    *max = a;
    *min = a;
    if (b > *max) { *max = b; } 
    if (c > *max) { *max = c; }
    if (d > *max) { *max = d; }
    if (b < *min) { *min = b; }
    if (c < *min) { *min = c; }
    if (d < *min) { *min = d; }
}


/**
 * @brief 
 * 
 * @param tick_objs 
 * @param oldest_tick  多少个tick之后，数据无效，自动移除
 */
void time_tick_init(spo2_time_tick_t *tick_objs,uint32_t oldest_tick)
{
    tick_objs->oldest_tick_limit = oldest_tick;

    tick_objs->hr_tick_buff_size = PEAK_VAl_BUFFER_LEN/2;
    tick_objs->tick_buff_size = SPO2_SAVE_ITEMS_SIZE ;
    tick_objs->tick_size = 0;
    tick_objs->hr_tick_size = 0;
    uint16_t i=0;
    for(i=0;i<tick_objs->tick_buff_size ;i++)
    {
        tick_objs->tick[i]=0;
        tick_objs->spo2[i]=0.0f;
        tick_objs->spo2_cache[i]=0.0f;
        tick_objs->spo2_shadow[i]=0.0f;
    }
    for(i=0;i<tick_objs->hr_tick_buff_size;i++)
    {
        tick_objs->hr_tick[i]=0;
        tick_objs->hr_timeRes[i]=0;
    }
}
/**
 * @brief 
 * 
 * @param tick_objs 
 * @param spo2_curt 添加数据 
 */
void time_tick_save(spo2_time_tick_t *tick_objs,float spo2_curt)
{
    ALG_PRINTF("[tick save info]: tick_size:[%d]\ttick_buff_size:[%d]\n",tick_objs->tick_size,tick_objs->tick_buff_size);
    if(tick_objs->tick_size>=(tick_objs->tick_buff_size))
    {
        for (size_t i = 1; i < tick_objs->tick_buff_size; i++)
        {
            tick_objs->tick[i-1] = tick_objs->tick[i];
            tick_objs->spo2[i-1] = tick_objs->spo2[i];
            ALG_PRINTF("[tick more] tick %ld val:[%f]\n",i-1,tick_objs->spo2[i-1]);
        }
          tick_objs->tick_size -=1;  
    }
    tick_objs->spo2[tick_objs->tick_size]=spo2_curt;
    tick_objs->tick[tick_objs->tick_size]=1;
    tick_objs->tick_size++;
    ALG_PRINTF("[tick save] tick size:[%d]\n",tick_objs->tick_size);
}


void time_tick_save_hr(spo2_time_tick_t *tick_objs,uint16_t hr_curt)
{
    if(tick_objs->hr_tick_size>=(tick_objs->hr_tick_buff_size))
    {
        ALG_PRINTF("[tick more HR][index][tick][HR]\n");
        for (uint8_t i = 1; i < tick_objs->hr_tick_buff_size; i++)
        {
            tick_objs->hr_timeRes[i-1] = tick_objs->hr_timeRes[i];
            tick_objs->hr_tick[i-1] = tick_objs->hr_tick[i];
            ALG_PRINTF("[%2d,%3d,%3d] ",i-1,tick_objs->hr_tick[i-1],tick_objs->hr_timeRes[i-1]);
        }
        ALG_PRINTF("\n");
            tick_objs->hr_tick_size -=1;  
    }
    tick_objs->hr_tick[tick_objs->hr_tick_size]=0;
    tick_objs->hr_timeRes[tick_objs->hr_tick_size]=hr_curt;
    tick_objs->hr_tick_size++;
    ALG_PRINTF("[tick save HR] tick size:[%d]\n",tick_objs->hr_tick_size);
}


/**
 * @brief 主函数更新tick
 * 
 * @param tick_objs 
 */
void time_ticks_update_tick(spo2_time_tick_t *tick_objs)
{
    uint16_t cutoff_n=0;
    for(uint16_t i=0;i<tick_objs->tick_size;i++)
    {   
        if(tick_objs->tick[i] >= tick_objs->oldest_tick_limit)
        {
            cutoff_n++;
        }
        tick_objs->tick[i]++; /// 更新tick值   
    }
    if(cutoff_n>0)
    {
        for (uint16_t i = cutoff_n; i < tick_objs->tick_size; i++)
        {
            tick_objs->tick[i-cutoff_n] = tick_objs->tick[i];
            tick_objs->spo2[i-cutoff_n] = tick_objs->spo2[i];
        }
        tick_objs->tick_size -= cutoff_n;  
    }
/******* HR info tick update *********/
    cutoff_n = 0;
    for(uint16_t i=0;i<tick_objs->hr_tick_size;i++)
    {
        if(tick_objs->hr_tick[i]>=tick_objs->oldest_tick_limit)
        {
            cutoff_n++;
        }
        tick_objs->hr_tick[i]++; /// 更新tick值   
    }
    if (cutoff_n>0)
    {
         for (uint16_t i = cutoff_n; i < tick_objs->hr_tick_size; i++)
        {
            tick_objs->hr_tick[i-cutoff_n] = tick_objs->hr_tick[i];
            tick_objs->hr_timeRes[i-cutoff_n] = tick_objs->hr_timeRes[i];
        }
        tick_objs->hr_tick_size -= cutoff_n;  
    }
    if(tick_objs->tick_size)
    {
        ALG_PRINTF("[tick show Spo2] item[%2d]\n",tick_objs->tick_size);
        ALG_PRINTF("-[index] ->\t");
        for(uint16_t i=0;i<tick_objs->tick_size;i++)
        {
            ALG_PRINTF("[%7d]",i);
            // ALG_PRINTF("[%d] tick %d val:[%f]\n",i,tick_objs->tick[i],tick_objs->spo2[i]);
        }
        ALG_PRINTF("\n");
        ALG_PRINTF("-[tick] ->\t");
        for(uint16_t i=0;i<tick_objs->tick_size;i++)
        {
            ALG_PRINTF("[%7d]",tick_objs->tick[i]);
            // ALG_PRINTF("[%d] tick %d val:[%f]\n",i,tick_objs->tick[i],tick_objs->spo2[i]);
        }
        ALG_PRINTF("\n");
        ALG_PRINTF("-[spo2] ->\t");
        for(uint16_t i=0;i<tick_objs->tick_size;i++)
        {
            ALG_PRINTF("[%2.4f]",tick_objs->spo2[i]);
            // ALG_PRINTF("[%d] tick %d val:[%f]\n",i,tick_objs->tick[i],tick_objs->spo2[i]);
        }
        ALG_PRINTF("\n");
    }
    // for(uint16_t i=0;i<tick_objs->tick_size;i++)
    // {
    //     ALG_PRINTF("[tick show Spo2] [%d] tick %d val:[%f]\n",i,tick_objs->tick[i],tick_objs->spo2[i]);
    // }
    // for(uint16_t i=0;i<tick_objs->hr_tick_size;i++)
    // {
    //     ALG_PRINTF("[tick show HR] [%d] tick %d val:[%d]\n",i,tick_objs->hr_tick[i],tick_objs->hr_timeRes[i]);
    // }
    if(tick_objs->hr_tick_size)
    {
        ALG_PRINTF("[tick show HR] item[%2d] [index] [tick] [HR]\n",tick_objs->hr_tick_size);
    ALG_PRINTF("-[index] ->\t");
    for(uint16_t i=0;i<tick_objs->hr_tick_size;i++)
    {
        ALG_PRINTF("[%3d]",i);
    }
    ALG_PRINTF("\n");
    ALG_PRINTF("-[tick] ->\t");
    for(uint16_t i=0;i<tick_objs->hr_tick_size;i++)
    {
        ALG_PRINTF("[%3d]",tick_objs->hr_tick[i]);
    }
    ALG_PRINTF("\n");
    ALG_PRINTF("-[HR] ->\t");
    for(uint16_t i=0;i<tick_objs->hr_tick_size;i++)
    {
        ALG_PRINTF("[%3d]",tick_objs->hr_timeRes[i]);
    }
    ALG_PRINTF("\n");
    }



}


/**
 * @brief 
 * -0.01   1.13
 * @param spo2 
 * @return float 
 */
float diff_coffient_gen(float spo2)
{
    float diff_coffient = 0.22f;
    if(spo2>=97.0f)
    {
        diff_coffient = 0.15f;
    }else if(spo2<85.0f)
    {
        diff_coffient = 0.29f;
    }else
    {
        diff_coffient = -0.01f*spo2+1.13f;
    }
    return diff_coffient*0.1f;
}
